#include "localsocket.hpp"
#include <sys/epoll.h>

namespace Local_Epool
{

    class EpoolServer
    {
        const static int _maxsize = 4096;
        const static int _defaultfd = -1;

    public:
        /**************构造析构************** */
        EpoolServer(int port)
            : _listensock(std::make_unique<Local_Socket::TcpSocket>()),
              _isrunning(false)
        {
            // 1.创建listensock套接字
            _listensock->ServerTcpMod(port);
            // 2.创建epoll模型
            _epfd = epoll_create(256);
            if (_epfd < 0)
            {
                Local_Log::LOG(Local_Log::LogLever::LFATAL) << "epoll_create error";
                exit(ExitCord::ERR_EPOLL_CREATE);
            }
            Local_Log::LOG(Local_Log::LogLever::LINFO) << "epoll_create success " << _epfd;

            // 3.创建epoll数据
            struct epoll_event lo_ev;
            lo_ev.events = EPOLLIN;
            // TODO : 这里未来是维护的是用户的数据，常见的是fd
            lo_ev.data.fd = _listensock->GetFd();
            // TODO : 这里未来是维护的是用户的数据，使用智能指针
            // ev.data.ptr = _listensock.get();
            // 4.将epoll模型设置到内核中！
            int ret_ctl = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->GetFd(), &lo_ev);
            if (ret_ctl < 0)
            {
                Local_Log::LOG(Local_Log::LogLever::LFATAL) << "epoll_ctl error";
                exit(ExitCord::ERR_EPOLL_CTL);
            }
            else
            {
                Local_Log::LOG(Local_Log::LogLever::LINFO) << "epoll_ctl success " << _epfd;
            }
        }
        ~EpoolServer()
        {
            _listensock->Close();
            if (_epfd > 0)
                close(_epfd);
        }
        /**************成员函数************** */
        void Start()
        {
            int lo_timeout = -1; // 阻塞或定时轮训

            _isrunning = true;
            while (_isrunning)
            {
                Local_Log::LOG(Local_Log::LogLever::LINFO) << "开始循环";

                // 1.epoll调用
                int ret_wai = epoll_wait(_epfd, _revs, _maxsize, lo_timeout);

                // 2.事件处理
                switch (ret_wai)
                {
                case -1:
                    Local_Log::LOG(Local_Log::LogLever::LERROR) << "poll error ";
                    break;
                case 0:
                    Local_Log::LOG(Local_Log::LogLever::LINFO) << "poll time out... ";
                    break;
                default:
                    Local_Log::LOG(Local_Log::LogLever::LDEBUG) << "poll 监听到了事件... -> ret_sel: " << ret_wai;
                    Dispatcher(ret_wai); // 处理就绪的事件啊！
                    break;
                }

                Local_Log::LOG(Local_Log::LogLever::LINFO) << "结束循环";
            }
            _isrunning = false;
        }
        void Stop()
        {
            _isrunning = false;
        }
        /**************事件函数************** */
        // 事件派发器
        void Dispatcher(int rnum)
        {
            Local_Log::LOG(Local_Log::LogLever::LINFO) << "event ready ..."; // LT: 水平触发模式--epoll默认
            for (int i = 0; i < rnum; i++)
            {
                // epoll也要循环处理就绪事件--这是应该的，本来就有可能有多个fd就绪！
                int lo_sockfd = _revs[i].data.fd;
                uint32_t lo_revent = _revs[i].events;
                // 通过revents判断
                if (lo_revent & EPOLLIN)
                {
                    if (lo_sockfd == _listensock->GetFd())
                    {
                        // listensockfd 新连接到来
                        Accepter();
                    }
                    else
                    {
                        // 普通的读事件就绪
                        Recver(lo_sockfd);
                    }
                }
            }
        }
        // 链接管理器
        void Accepter()
        {
            Local_Inetaddr::Inetaddr client;
            int lo_sockfd = _listensock->AcceptGetAndCreate(&client);
            if (lo_sockfd >= 0)
            {
                Local_Log::LOG(Local_Log::LogLever::LINFO) << "accept success";
                struct epoll_event lo_ev;
                lo_ev.events = EPOLLIN;
                lo_ev.data.fd = lo_sockfd;
                int ret_ctl = epoll_ctl(_epfd, EPOLL_CTL_ADD, lo_sockfd, &lo_ev);
                if (ret_ctl < 0)
                {
                    Local_Log::LOG(Local_Log::LogLever::LWARNING) << "epoll_ctl error";
                }
                else
                {
                    Local_Log::LOG(Local_Log::LogLever::LINFO) << "epoll_ctl success " << _epfd;
                }
            }
        }
        // IO处理器
        void Recver(int sockfd)
        {
            // 读取文件内容
            char lo_buffer[1024];
            int ret_rec = recv(sockfd, lo_buffer, sizeof(lo_buffer) - 1, 0);
            if (ret_rec > 0)
            {
                // 读取完成打印
                lo_buffer[ret_rec] = 0;
                std::cout << "client say@ " << lo_buffer << std::endl;
            }
            else if (ret_rec == 0)
            {
                // 文件描述符退出
                Local_Log::LOG(Local_Log::LogLever::LINFO) << "client exit";
                // 不要让select在关系这个fd了
                int ret_ctl = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
                if (ret_ctl > 0)
                {
                    Local_Log::LOG(Local_Log::LogLever::LINFO) << "epoll_ctl error";
                }
                // 关闭文件描述符
                close(sockfd);
            }
            else // if (ret_rec < 0)
            {
                // 读取错误
                Local_Log::LOG(Local_Log::LogLever::LERROR) << "recv error";
                // 不要让select在关系这个fd了
                int ret_ctl = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
                if (ret_ctl > 0)
                {
                    Local_Log::LOG(Local_Log::LogLever::LINFO) << "epoll_ctl error";
                }
                // 关闭文件描述符
                close(sockfd);
            }
        }
        /**************GetSet************** */
        /**************Debug************** */

    private:
        std::unique_ptr<Local_Socket::Socket> _listensock;
        bool _isrunning;

        int _epfd;
        struct epoll_event _revs[_maxsize];
    };

}